package com.kevin.epacms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kevin.epacms.constant.CommonConstant;
import com.kevin.epacms.entity.Upload;
import com.kevin.epacms.entity.vo.ConditionVo;
import com.kevin.epacms.entity.vo.UploadVo;
import com.kevin.epacms.mapper.UploadMapper;
import com.kevin.epacms.service.UploadService;
import com.kevin.epacms.util.PageList;
import com.kevin.epacms.util.Result;
import com.kevin.epacms.util.SearchUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author kevin
 * @since 2022-11-10
 */
@Slf4j
@Service
public class UploadServiceImpl extends ServiceImpl<UploadMapper, Upload> implements UploadService {
    @Value("${uploadFile.path}")
    private String folder;

    @Override
    public Result uploadFile(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        // 获取扩展名
        String fileType = FileUtil.extName(originalFilename);
        long fileSize = file.getSize();
        // 指定目录
        File imageFolder = new File(folder);
        // 唯一标识符.扩展名
        String fileUuid = IdUtil.fastSimpleUUID();
        String date = LocalDate.now().toString();
        File uploadFile = new File(imageFolder + StrUtil.SLASH + date + StrUtil.SLASH, fileUuid + StrUtil.DOT + fileType);
        // 判断配置文件目录是否存在，若不存在则创建一个
        if (!uploadFile.getParentFile().exists()) {
            if (!uploadFile.getParentFile().mkdir()) {
                log.info("{}目录创建失败", uploadFile.getAbsolutePath());
            }
        }

        try {
            String url;
            // 把获取到的文件存储到磁盘
            file.transferTo(uploadFile);
            String md5 = SecureUtil.md5(uploadFile);
            // 通过 md5 获取文件
            Upload fileDb = getFileByMd5(md5);
            if (Objects.nonNull(fileDb)) {
                url = fileDb.getUrl();
                // 由于文件已经存在，所以删除刚才上传的重复文件
                if (uploadFile.delete()) {
                    log.info("重复文件{}，丢弃", fileDb);
                }
            } else {
                // 若文件没有重复，生成url
                url = CommonConstant.BASE_URL + fileUuid;
            }
            Upload upload = new Upload();
            upload.setName(originalFilename);
            upload.setType(fileType);
            upload.setSize(fileSize / 1024);
            upload.setUrl(url);
            upload.setMd5(md5);
            upload.setFileUuid(fileUuid);
            upload.setUploadTime(LocalDateTime.now());
            // 存入数据库
            if (!save(upload)) {
                log.warn("{}文件保存失败", uploadFile.getAbsoluteFile());
            }
            return Result.ok(CommonConstant.SUCCESS, url);
        } catch (IOException e) {
            log.warn("文件上传异常: {}", e.getMessage());
            return Result.fail("文件上传异常");
        }
    }

    @Override
    public void download(String fileUuid, HttpServletResponse response) {
        Upload upload = lambdaQuery().eq(Upload::getFileUuid, fileUuid).one();
        if (Objects.isNull(upload)) {
            log.info("数据库没有保存{}文件，下载失败", fileUuid);
            return;
        }
        String date = upload.getUploadTime().toLocalDate().toString();
        File downloadFile = new File(folder + date + StrUtil.SLASH + fileUuid + StrUtil.DOT + upload.getType());
        try {
            if (!downloadFile.exists()) {
                log.warn("文件系统中没有此文件: {}", downloadFile.getPath());
                return ;
            }
            //从磁盘获取到文件
            ServletOutputStream os = response.getOutputStream();
            //设置输出流的格式
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(upload.getName(), "UTF-8"));
            response.setContentType("application/octet-stream");
            os.write(FileUtil.readBytes(downloadFile));
            os.flush();
            os.close();
        } catch (IOException e) {
            log.warn("文件下载异常: {}", e.getMessage());
        }
    }

    @Override
    public Result deleteFileById(Long id) {
        boolean update = lambdaUpdate().eq(Upload::getId, id).set(Upload::getIsDelete, true).update();
        return update ? Result.ok(CommonConstant.SUCCESS) : Result.fail(CommonConstant.FAIL);
    }

    @Override
    public Result deleteBatch(String idsStr) {
        if(StrUtil.isBlank(idsStr)){
            return Result.fail("请选择要删除的文件id");
        }
        List<Long> ids = Arrays.stream(idsStr.split(StrUtil.COMMA)).map(Long::parseLong).collect(Collectors.toList());
        boolean flag = lambdaUpdate().in(Upload::getId, ids).setSql("is_delete = 1").update();
        return flag ? Result.ok(CommonConstant.SUCCESS) : Result.fail(CommonConstant.FAIL);
    }

    @Override
    public Result getFilesByPage(Integer currentPage, Integer pageSize, List<ConditionVo> conditions) {
        QueryWrapper<Upload> queryWrapper = SearchUtil.parseWhereSql(conditions);
        Page<Upload> page =  baseMapper.selectPage(new Page<>(currentPage, pageSize), queryWrapper.eq("is_delete", 0));
        PageList pageList = new PageList(page);
        pageList.setDataList(BeanUtil.copyToList(pageList.getDataList(), UploadVo.class));
        return Result.ok(pageList);
    }

    private Upload getFileByMd5(String md5) {
        List<Upload> imgFiles = lambdaQuery().eq(Upload::getMd5, md5).list();
        return imgFiles.size() > 0 ? imgFiles.get(0) : null;
    }
}
